﻿namespace FxCopContrib
{
    using System;
    using System.Collections.Generic;
    using System.Reflection;
    using Microsoft.FxCop.Sdk;
    using System.Globalization;
    using System.IO;
    using Microsoft.FxCop.Common;
    using System.Linq;

    /// <summary>
    /// This class contains any helper methods that might be useful for other rules
    /// </summary>
    internal static class RuleHelper
    {
        private static object SyncRoot = new object();
        private static List<string> immutableTypes;

        /// <summary>
        /// The Microsoft.SecurityRules assembly contains a standard method to return a pre-defined list of Immutable types that are present
        /// in the Framework libraries. This list can easily be extended with custom types using the ImmutableTypes.txt. There is however
        /// no standard method available to get this list of types, the only method which is available is marked Internal.
        /// </summary>
        /// <remarks>
        /// This method uses Reflection to call into the microsoft Security Rules assembly. This means that this rule only works properly if 
        /// said assembly is available. It is also an expensive call, so cache the outcome of this method.
        /// 
        /// The standard list does not contain TimeSpan and DateTimeOffset, as these are prime examples of the types we want to catch 
        /// int he DoNotIgnoreResultOfImmutableMethods, I've added them to the list statically.
        /// </remarks>
        /// <returns></returns>
        public static IList<string> RetreiveImmutableTypes()
        {
            if (immutableTypes == null)
            {
                lock (SyncRoot)
                {
                    if (immutableTypes == null)
                    {
                        immutableTypes = new List<string>();
                        LoadDefaultImmutableTypes();
                        LoadImmutableTypesFromFolder(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
                        LoadImmutableTypesFromFolder(Path.GetDirectoryName(typeof(FxCopCategoryInfo).Assembly.Location));
                        immutableTypes = immutableTypes.Distinct().ToList();
                    }
                }
                
            }
            return immutableTypes;
        }

        private static void LoadImmutableTypesFromFolder(string basepath)
        {
            string immutaleTypesFile = Path.Combine(basepath, "ImmutableTypes.txt");
            if (File.Exists(immutaleTypesFile))
            {
                using (var fileStream = File.OpenRead(immutaleTypesFile))
                {
                    LoadImmutableTypedFromStream(fileStream);
                }
            }
        }

        private static void LoadDefaultImmutableTypes()
        {
            Assembly assembly = Assembly.Load("SecurityRules");
            using (var stream = assembly.GetManifestResourceStream("Microsoft.FxCop.Rules.Security.Resources.ImmutableTypes.txt"))
            {
                immutableTypes.Add(FrameworkTypes.TimeSpan.FullName);
                immutableTypes.Add(FrameworkTypes.DateTimeOffset.FullName);

                LoadImmutableTypedFromStream(stream);
            }
        }

        private static void LoadImmutableTypedFromStream(Stream stream)
        {
            using (StreamReader reader = new StreamReader(stream))
            {
                string type = null;
                while ((type = reader.ReadLine()) != null)
                {
                    immutableTypes.Add(type);
                }
            }
        }
    }
}
